import { $$observable } from "./utils/symbol-observable";
import { isPlainObject } from "./utils/isPlainObject";
import { ActionTypes } from "./utils/actionTypes";

/**
 * 创建仓库数据
 * @param {*} reducer 
 * @param {*} defaultState 默认状态
 * @param {*} enhancer 中间件
 */
export function createStore(reducer, defaultState, enhancer) {
    // 判断传入数据可用性并确定参数位置
    if (
        (typeof defaultState === 'function' && typeof enhancer === 'function') || (typeof enhancer === 'function' && typeof arguments[3] === 'function')
    ) {
        throw new Error(`It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.`)
    }
    if (typeof defaultState === 'function' && typeof enhancer === 'undefined') {
        enhancer = defaultState;
        defaultState = undefined;
    }
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
            // 中间件有值但是不是函数直接抛出错误
            throw new Error('Expected the enhancer to be a function.')
        }
        // enhancer 就是 applyMiddleware 中间件函数得到返回函数
        return enhancer(createStore)(reducer, defaultState); // 中间件使用用于增强函数
    }
    // reducer 不是函数直接抛出错误
    if (typeof reducer !== 'function') {
        throw new Error('Expected the reducer to be a function.')
    }
    let currentReducer = reducer; // 保存 reducer 
    let currentState = defaultState; // 保存默认状态
    const listeners = [];
    /**
     * 派发更改数据
     * @param {*} action  描述对象
     * @returns 
     */
    function dispatch(action) {
        // action 描述对象必须是平面对象
        if (!isPlainObject(action)) {
            throw new Error(`Actions must be plain objects. Use custom middleware for async actions.`);
        }
        // 验证描述对象必须有 type 属性
        if (typeof action.type === 'undefined') {
            throw new Error(`Actions may not have an undefined "type" property. Have you misspelled a constant?`);
        }
        try {
            currentState = currentReducer(currentState, action); // 更新状态
        } catch (e) {
            throw new Error(e.toString());
        }
        // 运行所有订阅者
        for (const listener of listeners) {
            listener();
        }
        return action
    }
    /**
     * 添加一个监听器
     * @param {*} listener 监听函数
     * @returns 取消监听函数
     */
    function subscribe(listener) {
        // 判断传入监听器是否为函数
        if (typeof listener !== 'function') {
            throw new Error('Expected the listener to be a function.');
        }
        let isSubscribed = false; // 加标识多次取消运行
        listeners.push(listener); // 存储订阅者
        // 返回取消监听函数
        return () => {
            if (isSubscribed) return;
            const index = listeners.indexOf(listener);
            listeners.splice(index, 1);
            isSubscribed = true;
        }
    }
    /**
     * 获取当前仓库状态数据
     * @returns 仓库数据
     */
    function getState() {
        return currentState
    }
    /**
     * 替换当前仓库 reducer
     * @param {*} nextReducer 
     */
    function replaceReducer(nextReducer) {
        // 判断 reducer 是否函数
        if (typeof nextReducer !== 'function') {
            throw new Error('Expected the nextReducer to be a function.')
        }
        currentReducer = nextReducer; // 替换当前 reducer
        dispatch({ type: ActionTypes.REPLACE }); // 派发一次
    }
    function observable() {
        const outerSubscribe = subscribe; // 监听函数
        return {
            subscribe(observer) {
                if (typeof observer !== 'object' || observer === null) {
                    throw new TypeError('Expected the observer to be an object.')
                }
                function observeState() {
                    if (observer.next) {
                        observer.next(getState())
                    }
                }
                observeState()
                const unsubscribe = outerSubscribe(observeState)
                return { unsubscribe }
            },
            [$$observable]() {
                return this
            },
        }
    }
    //! 初始化需派发
    dispatch({ type: ActionTypes.INIT });
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable //? 此方法为私有外部一般操作获取不到
    }
}